package vendingMachine;
import static org.junit.Assert.*;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 * 
 */

/**
 * @author Liqun Zhang
 *
 */
public class VendingMachineTest {
	VendingMachine vm;
	VendingMachineItem valid_item;
	VendingMachineItem free_item;
	/**
	 * Setup common fixture: a vendingmachine; a vendingmachineitem with price>0
	 * and a item with price=0
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		 vm = new VendingMachine();
		 valid_item = new VendingMachineItem ("cookies", 1.00);
		 free_item = new VendingMachineItem("free", 0.00);
		
	}

	/**
	 * @throws java.lang.Exception
	 */
	@After
	public void tearDown() throws Exception {
		 vm = null;
	}

	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * Test adding an item to a empty slot with a valid code
	 */
	@Test
	public void testAddItem() {
		vm.addItem(valid_item, "A");
		assertEquals(valid_item, vm.getItem("A"));
	}
	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * Test adding an item to a slot which is already occupied
	 */
	@Test(expected = VendingMachineException.class)
	public void testAddItemOccupied() {
		vm.addItem(valid_item, "A");
		vm.addItem(valid_item, "A");
	}
	/**
	 * Test method for {@link VendingMachine#addItem(VendingMachineItem, java.lang.String)}.
	 * Test adding an item to a empty slot with a invalid code
	 */
	@Test(expected = VendingMachineException.class)
	public void testAddItemInvalidCode() {
		vm.addItem(valid_item, "E");
	}

	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * Test removing an item from a slot which is occupied
	 */
	@Test
	public void testRemoveItem() {
		vm.addItem(valid_item, "A");
		assertEquals(valid_item, vm.removeItem("A"));
		assertNull(vm.getItem("A"));
	}
	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * Test removing an item from a empty slot
	 */
	@Test (expected = VendingMachineException.class)
	public void testRemoveItemEmpty() {
		vm.removeItem("A");
	}
	/**
	 * Test method for {@link VendingMachine#removeItem(java.lang.String)}.
	 * Test removing an item from a slot with a invalid code
	 */
	@Test (expected = VendingMachineException.class)
	public void testRemoveItemInvalidCode() {
		vm.removeItem("E");
	}

	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * Test InsertMoney method with amount >0 and insert money once
	 */
	@Test
	public void testInsertMoney() {
		vm.insertMoney(5.00);
		assertEquals(5.00,vm.getBalance(),0);
	}
	@Test
	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * Test InsertMoney method with amount=0
	 */
	public void testInsertMoneyZero() {
		vm.insertMoney(0);
		assertEquals(0,vm.getBalance(),0);
	}
	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * Test InsertMoney method with amount >0 and insert money several times
	 */
	@Test
	public void testInsertMoneyManyTimes() {
		vm.insertMoney(0.25);
		vm.insertMoney(0.25);
		vm.insertMoney(0.25);
		vm.insertMoney(0.25);
		assertEquals(1.00,vm.getBalance(),0);
	}
	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * Test InsertMoney method with amount <0 and insert money once
	 */
	@Test (expected = VendingMachineException.class)
	public void testInsertMoneyNegativeAmount() {
		vm.insertMoney(-1.25);
	}
	/**
	 * Test method for {@link VendingMachine#insertMoney(double)}.
	 * Test Inserting money twice. The amount is >0 for the first time
	 * and the amount is <0 for the second time
	 */
	@Test (expected = VendingMachineException.class)
	public void testInsertMoneyInvalidAmount() {
		vm.insertMoney(1.00);
		vm.insertMoney(-0.25);
		assertEquals(1.00,vm.getBalance(),0);
	}

	/**
	 * Test method for {@link VendingMachine#getBalance()}.
	 * The balance is >0
	 */
	@Test
	public void testGetBalance() {
		vm.insertMoney(1.00);
		double i = vm.getBalance();
		assertEquals(1.00,i,0);
	}
	/**
	 * Test method for {@link VendingMachine#getBalance()}.
	 * The balance is =0
	 */
	@Test
	public void testGetBalanceZero() {
		assertEquals(0,vm.getBalance(),0);
	}

	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * Test purchasing item successfully. The item exists and inserting enough money
	 */
	@Test
	public void testMakePurchase() {
		vm.addItem(valid_item,"A");
		vm.insertMoney(5.00);
		assertTrue(vm.makePurchase("A"));
		assertNull(vm.getItem("A"));
		assertEquals(4.00,vm.getBalance(),0);
		
	}
	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * Test purchasing free item successfully. The item exists and it is free
	 */
	@Test
	public void testMakePurchaseFreeItem() {
		vm.addItem(free_item,"A");
		assertTrue(vm.makePurchase("A"));
		assertNull(vm.getItem("A"));
		assertEquals(0.00,vm.getBalance(),0);
		
	}
	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * Test purchasing item successfully through inserting money several times. 
	 * Insert insufficient money for the first time and purchase unsuccessfully
	 * then insert remaining money and purchase successfully in the end 
	 */
	@Test
	public void testMakePurchaseManyInsertMoney() {
		vm.addItem(valid_item,"A");
		vm.insertMoney(0.75);
		assertFalse(vm.makePurchase("A"));
		vm.insertMoney(0.25);
		assertTrue(vm.makePurchase("A"));
		assertNull(vm.getItem("A"));
		assertEquals(0.00,vm.getBalance(),0);			
	}
	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * Test purchasing item unsuccessfully because of insufficient money 
	 */
	@Test
	public void testMakePurchaseLessMoney() {
		vm.addItem(valid_item,"A");
		vm.insertMoney(0.75);
		assertFalse(vm.makePurchase("A"));
		assertEquals(valid_item,vm.getItem("A"));
		assertEquals(0.75,vm.getBalance(),0);
		
	}
	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * Test purchasing item which doesn't exist. 
	 */
	@Test
	public void testMakePurchaseEmpty() {
		vm.insertMoney(0.75);
		assertFalse(vm.makePurchase("A"));
		
	}
	/**
	 * Test method for {@link VendingMachine#makePurchase(java.lang.String)}.
	 * Test purchasing item by given invalid code. 
	 */
	@Test (expected = VendingMachineException.class)
	public void testMakePurchaseInvalidCode() {
		vm.addItem(valid_item,"A");
		vm.insertMoney(1.00);
		vm.makePurchase("E");
	}

	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 * Test returning change which is >0
	 */
	@Test
	public void testReturnChange() {
		vm.addItem(valid_item, "A");
		vm.insertMoney(5.00);
		vm.makePurchase("A");
		assertEquals(4.00,vm.returnChange(),0);
		assertEquals(0,vm.getBalance(),0);
	}
	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 * Test returning change which is =0
	 */
	@Test
	public void testReturnChangeZero() {
		vm.addItem(valid_item, "A");
		vm.insertMoney(1.00);
		vm.makePurchase("A");
		assertEquals(0,vm.returnChange(),0);
		assertEquals(0,vm.getBalance(),0);
	}
	/**
	 * Test method for {@link VendingMachine#returnChange()}.
	 * Test returning change when buying free item
	 */
	@Test
	public void testReturnChangeFreeItem() {
		vm.addItem(free_item, "A");
		vm.makePurchase("A");
		assertEquals(0,vm.returnChange(),0);
		assertEquals(0,vm.getBalance(),0);
	}

}
